// 基本类型

// Boolean 类型
let isOk: boolean = false;
isOk = true;

// Number 类型
let decimal: number = 10;
let intNum: number = 20;
let doubleNum: number = 13.14;
// let bigNum: bigint = 100n;  // bigint ES2020 语法

// String
let colorBlue: string = "blue";
let message: string = "This is a message.";

// 使用模板字符串
let fullName: string = `Bob Bobbington`;
let age1: number = 30;
let sentence2: string = `Hello, my name is ${fullName}.

I'll be ${age1 + 1} years old next month.`

// Array

// 定义数组的第一种方式 类型[]
let array: number[] = [1, 2, 3];

// 定义数组的第二种方式 Array<类型>
let array2: Array<string> = ["Hello", "TypeScript"];

// Tuple
// 元组类型：数组中的元素个数确定，元素的类型也确定
let tuple: [string, number, boolean];
tuple = ["张三", 20, true];

// 枚举类型
// 默认情况下，枚举为元素从0开始编号
// 可以手动设置一个值来改变此规则
// 例如：可以 设置枚举元素从1开始编号
enum Colors {
  Red = 1,
  Green,
  Yellow
}

let c : Colors = Colors.Red

// 也可以手动设置枚举编号
enum PlayerState {
  Start = 1,
  Suspend = 2,
  Stop = 4
}

let playState: string = PlayerState[2];
console.log(playState); // Suspend

// Unknown
// 不知道变量时什么类型是使用 unknown 类型
let notSure1: unknown = 4;
notSure1 = "Do you understand?";
notSure1 = false;

// declare 声明变量用于类型检查
declare const maybe: unknown;

// error TS2322: Type 'unknown' is not assignable to type 'number'.
// 错误：不能将 unknown 类型赋值给 number
// const aNumber: number = maybe;

if (maybe === true) {
  // 添加判断之后可以赋值
  const aBoolean: boolean = maybe;

  // error TS2322: Type 'boolean' is not assignable to type 'string'.
  // 错误：不能将 boolean 类型赋值给 string
  // const aString: string = maybe;
}

if (typeof maybe === 'string') {
  const aString: string = maybe;
}

// Any
// any 类型是处理现有 JavaScript 的一种强大的方法
// 允许在编译期间加入和退出类型检查
// 与 Unknown 类型不同，any类型的变量允许您访问任意属性，甚至是不存在的属性。
// 这些属性包括函数，TypeScript不会检查它们的存在或类型
declare function getValue(key: string): any;
const value: string = getValue("key");

let looselyTyped: any = 4;
// OK, ifItExists might exist at runtime
looselyTyped.ifItExists();
// OK, toFixed exists (but the compiler doesn't check)
looselyTyped.toFixed();

let strictlyTyped: unknown = 4;
// error TS2571: Object is of type 'unknown'.
// strictlyTyped.toFixed();

// Void
// 函数中，没有返回值类型使用 void
function warnUser(message: string): void {
  console.log(`[WARN] ${message}`);
}

// void 类型的变量没有意义，因为只能将其赋值为 undefined
// 在没有指定 strictNullChecks 的情况下可以赋值为 null
let unusable2: void = undefined;
unusable2 = undefined;

// Null and Undefined
// 默认情况下，null和undefined是所有其他类型的子类型。
// 可以将 null 和 undefined 赋值给其他类型
// 但是，在使用 --strictNullChecks 标志时，null 和 undefined 只可分配给 unknown、any
//  及其各自的类型（唯一的例外是undefined也可以赋值给void）
// 如果要传入 string、null 或 undefined，可以使用 union 类型 string | null | undefined。
let nullVar: null = null;

// error TS2322: Type 'undefined' is not assignable to type 'null'.
// nullVar = undefined;

let undefinedVar: undefined = undefined;

// error TS2322: Type 'null' is not assignable to type 'undefined'.
// undefinedVar = null;

// Never
// never 是函数表达式的返回类型，或总是抛出异常或从不返回的箭头函数表达式的返回类型。
function throwError(message: string): never {
  throw new Error(message);
}

function BusinessFail(): never {
  return throwError("业务逻辑错误");
}

// Function returning never must not have a reachable end point
function infiniteLoop2(): never {
  while (true) {}
}

// Object
// 对象是一个表示非基元类型的类型，即任何非数字、字符串、布尔值、bigint、symbol、null或未定义的类型。
declare function create(o: object | null): void;

create({});
create(null);
create(32);

// Type assertions
// 类型断言
// 类型断言类似与其他语言中的类型转换
let someValue: unknown = "This is a string message";

// error TS2571: Object is of type 'unknown'.
// 直接写会报错
// let someLength: number = someValue.length;

// 使用类型断言
let someLength: number = (someValue as string).length;

// 老版本的 <类型> 语法
let someLength2: number = (<string>someValue).length;

// 类型断言的两种形式都可以选择，但是 当在 JSX 中使用 TypeScript 时，只允许使用 as 。

// About Number, String, Boolean, Symbol and Object
// 您可能会认为 Number、String、Boolean、Symbol或Object 类型与上面推荐的小写版本相同。
// 但是，这些类型不引用语言原始类型，而且几乎永远不应该用作类型

function reverse(s: String): String {
  return s.split("").reverse().join("");
}

reverse("hello world");

// 相反，请使用类型 number、string、boolean、object和symbol。
// 推荐使用小写类型
function reverse2(s: string): string {
  return s.split("").reverse().join("");
}

reverse("hello world");
